#include <list>
#include <iostream>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_projection.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Entity.hpp"

#ifndef __NODE_HPP__
#define __NODE_HPP__

class Node;

typedef pair<glm::vec3, Node*>  childNode_t;
typedef list<childNode_t> childNodeList_t;


class Node {
  protected:
    glm::vec3 m_position;
    glm::vec3 m_speed;
    glm::vec3 m_target;
    glm::vec3 m_direction;
    float m_angle;
    Entity* m_attachedEntity;
    childNodeList_t m_childNodeList;
    Node* m_parentNode;
  public:
	/** constructor : node parameter initialization */
    Node() {
      m_position = m_target = m_speed = glm::vec3(0,0,0);
      m_direction = glm::vec3(0.f,1.f,0.f);
      m_angle = 0.f;
      m_attachedEntity = 0;
      m_parentNode = 0;
    };

    /*** update function 
     * @param time , receive the elapsed time since last call to update
     */
    void update(float time);

	mat4 getModelViewMatrix() {
		glm::mat4 tmp = glm::translate(glm::mat4(1.0f), m_position);//glm::vec3(m_position.x, m_position.y, m_position.z));
		tmp = glm::rotate(tmp, m_angle, vec3(0.0, 0.0, 1.0));
		return tmp;
	};


    /**
     *  render function, call to render Node entities in the framebuffer
     */
    void render();

    /**
     * function to attach an Entity to this Node, the entity will be update
     * by the node update function and render at the Node position by the node 
     * render function
     * @param ent  Entity* to be attached
     */
    void attachEntity(Entity* ent) { m_attachedEntity = ent;}; 
    void setTarget(glm::vec3 target) {  m_target = target;};
    void setPosition(glm::vec3 position) { m_position = position;};
    void setDirection(glm::vec3 direction) { m_direction = direction;};
    void setSpeed(glm::vec3 speed) { m_speed = speed;};
    void setAngle(float angle) { m_angle = angle;};

    glm::vec3 getTarget() { return m_target; };
    glm::vec3 getPosition() { return m_position; };
    glm::vec3 getDirection() { return m_direction; };
    glm::vec3 getSpeed() { return m_speed; };
    Entity* getEntity() { return m_attachedEntity;};
    float getAngle() { return m_angle; };

    /** function to attach a child Node to this one, with an offset vector
     * @param dec  offset vector to position child Node
     * @param node child Node* to attach
     */
    childNodeList_t::iterator attachChildNode(glm::vec3 dec ,Node* node) { 
      node->setParent(this);
      return m_childNodeList.insert(m_childNodeList.end(),make_pair(dec,node));
    };

    /** function to link this Node to his parent Node (public access to the private member m_parentNode)
     * @param node parent node to be linked to
     */
    void setParent(Node* node) { m_parentNode = node;};

};


#endif
